
PM_FUNC_NORM_ATTRIB ScriptInit()
{
	pokemod_funcstart_debug( "ScriptInit" );

	SetupTrie( g_iFunctionTrie );
	SetupArray( g_iScriptEncounters, 1 );
	SetupTrie( g_iSayCmdsTrie );

	new String:szScriptFuncName[ SCRIPTFUNC ][] = {
		"HavePoke",
		"HaveItem",
		"HaveSkill",
		"HaveSpecial",
		"Version",
		"Chance",
		"AdminFlag",

		"CreateMenu",
		"MenuItem",
		"MenuShopItem",
		"MenuExit",
		"ShowCustomMenu",

		"BuyItem",
		"GiveItem",
		"FindItem",
		"LearnSkill",
		"BuyPoke",
		"GivePoke",
		"GiveXP",
		"GiveLevel",
		"Evolve",
		"GiveSpecial",
		"TakeSpecial",
		"PokeCenter",
		"HealHP",
		"CureStatus",
		"ShowMenu",
		"Function",
		"TeleportTo",
		"MoveTo",
		"MoveUpdate",
		"MoveStop",
		"GiveToPoke",
		"EncounterPokemon",
		"FightEncounter",
		"RemoveEncounter",
		"Fighting",
		"Surfing",
		"EventPrint",
		"EventPrintAll",
		"Print",
		"ViewMap",
		"Random",
		"RandomNum",
		"ItemUsed",
		"StatBoost",
		"Command",
		"If",
		"And",
		"Or",
		"RegisterSayCmd",
		"DisplayMOTD"
	}

	for ( new SCRIPTFUNC:iFuncNum; iFuncNum < SCRIPTFUNC; iFuncNum++ )
		TrieSetCell( g_iFunctionTrie, szScriptFuncName[ iFuncNum ], iFuncNum );

	ScriptCustomFunction( .iPlayer = 0, .szFunctionName = "Initialize" );

	pokemod_funcend_debug( "ScriptInit" );
}

PM_FUNC_NORM_ATTRIB ScriptLineForward( String:szData[], String:szForwardName[], Int:forwardLen, String:szLine[], Int:lineLen )
{
	pokemod_funcstart_debug( "ScriptLineForward" );

	new Int:i, Int:iParenthesis;
	for ( new Int:j; j < forwardLen && szData[ i ] != EOS; i++ )
	{
		switch( szData[ i ] )
		{
			case ' ':
			{
				if ( !iParenthesis )
				{
					szForwardName[ j ] = EOS;
					i++;
					break;
				}
				else
				{
					szForwardName[ j ] = szData[ i ];
					j++;
				}
			}
			case '(':
			{
				iParenthesis++;
				szForwardName[ j ] = szData[ i ];
				j++;
			}
			case ')':
			{
				iParenthesis--;
				szForwardName[ j ] = szData[ i ];
				j++;
			}
			default:
			{
				szForwardName[ j ] = szData[ i ];
				j++;
			}
		}
	}

	copy( szLine, lineLen, szData[ i ] );

	pokemod_funcend_debug( "ScriptLineForward" );
}

PM_FUNC_NORM_ATTRIB ScriptCompareForward( String:szFileForward[], String:szForward[], String:szArgument[], bool:plus )
{
	//szFileForward is read from the file, this also contains an argument
	//Example: "GiveItem( Fire Stone )"

	//szForward is the one from PokeMod
	//Example: "GiveItem"

	//szArgument is the one from PokeMod
	//Example: "Fire Stone"

	new Int:parenthesisLoc = contain( szFileForward, "(" );
	if ( parenthesisLoc != -1 )
	{
		if ( !equali( szFileForward, szForward, parenthesisLoc ) )
			return 0;

		new String:szFileArg[ NAME_LEN ];
		copy( szFileArg, charsmax(szFileArg), szFileForward[ parenthesisLoc + 1 ] );
		replace_all( szFileArg, charsmax(szFileArg), " ", "" );
		replace_all( szFileArg, charsmax(szFileArg), "_", "" );
		new Int:len = strlen( szFileArg );
		if ( szFileArg[ len - 1 ] == ')' )
			szFileArg[ len - 1 ] = EOS;

		new String:szArg[ NAME_LEN ];
		copy( szArg, charsmax(szArg), szArgument );
		replace_all( szArg, charsmax(szArg), " ", "" );
		replace_all( szArg, charsmax(szArg), "_", "" );

		if ( equali( szFileArg, szArg ) )
			return 1;

		if ( plus && contain( szFileArg, "+" ) != -1 )
		{
			if ( str_to_num( szFileArg ) <= str_to_num( szArg ) )
				return 1;
		}

		return 0;
	}

	return equali( szFileForward, szForward );
}

PM_FUNC_NORM_ATTRIB ScriptDoLine( Int:iPlayer, String:szLine[] )
{
	if ( szLine[ 0 ] == ';' )
		return 0;

	new String:szStatement[ 256 ], Int:iStatements, Int:iParenthesis, Int:j;
	for ( new Int:i; szLine[i] != EOS; i++ )
	{
		szStatement[j] = szLine[i];
		j++;

		switch( szLine[i] )
		{
			case '/':
			{
				if ( szLine[ i + 1 ] == '/' )
				{
					break;
				}
			}
			case '(':
			{
				iParenthesis++;
			}
			case ')':
			{
				iParenthesis--;
				if ( !iParenthesis )
				{
					szStatement[j] = EOS;
					iStatements++;
					if ( !ScriptAnalyze( iPlayer, szStatement ) )
						return 0;
					j = 0;
				}
				else if ( iParenthesis < 0 )
				{
					pokemod_debug( "ScriptDoLine error doing line: %s", szLine );
					return 0;
				}
			}
		}
	}

	if ( !iStatements )
	{
		szStatement[j] = EOS;
		return ScriptAnalyze( iPlayer, szStatement );
	}

	return 1;
}

PM_FUNC_NORM_ATTRIB ScriptAnalyze( Int:iPlayer, String:szStatement[] )
{
	pokemod_funcstart_debug( "ScriptAnalyze" );

	trim( szStatement );

	new bool:bReturnTruth = true;
	new Int:iFunctionStart;

	switch( szStatement[0] )
	{
		case '+':
		{
			iFunctionStart = 1;
			bReturnTruth = true;
		}
		case '-','!':
		{
			iFunctionStart = 1;
			bReturnTruth = false;
		}
		case EOS:
		{
			pokemod_funcend_debug( "ScriptAnalyze Null Statement" );
			return false;
		}
	}

	new bool:bRealTruth = bool:ScriptFunction( iPlayer, szStatement[ iFunctionStart ], strlen( szStatement[ iFunctionStart ] ) );

	pokemod_funcend_debug( "ScriptAnalyze" );

	return bReturnTruth ? bRealTruth : !bRealTruth;
}

PM_FUNC_NORM_ATTRIB ScriptFunction( Int:iPlayer, String:szFunction[], Int:len )
{
	pokemod_funcstart_debug( "ScriptFunction '%s'", szFunction );

	if ( contain( szFunction, "%" ) != -1 )
	{
		if ( iPlayer > 0 )
		{
			replace_all( szFunction, len, "%player%", PlayerToName( iPlayer ) );
		}

		if ( g_iScriptItem > -1 )
		{
			replace_all( szFunction, len, "%item%", ItemToName( g_iScriptItem ) );
		}

		if ( g_iScriptPokeNum > -1 )
		{
			replace_all( szFunction, len, "%pokemon%", PokeNumToName( g_iScriptPokeNum, .playerID = iPlayer ) );
		}

		replace_all( szFunction, len, "%activepokemon%", PokeNumToName( PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM ), .playerID = iPlayer ) );
	}

	new String:szFuncName[ 64 ];
	new String:szFuncArgs[ SCRIPT_MAX_ARGS ][ 128 ];
	new Int:iArgCount;

	copy( szFuncName, charsmax( szFuncName ), szFunction );

	new Int:iFuncNameEnd = contain( szFuncName, "(" );

	#define szAllArgs szFunction[ iFuncNameEnd + 1 ]

	if ( iFuncNameEnd != -1 )
	{
		szFuncName[ iFuncNameEnd ] = EOS;

		if ( szFunction[ len-1 ] == ')' )
		{
			len--;
			szFunction[ len ] = EOS;
		}

		new Int:iParenthesis;
		for ( new Int:j, Int:i = iFuncNameEnd + 1;
			i < len;
			i++
		){
			switch( szFunction[ i ] )
			{
				case ',':
				{
					if ( !iParenthesis )
					{
						if ( i && j && szFunction[ i-1 ] == '\' )
						{
							j--;
						}
						else
						{
							szFuncArgs[ iArgCount ][ j ] = EOS;
							iArgCount++;
							j = 0;

							if ( iArgCount == SCRIPT_MAX_ARGS )
								break;

							continue;
						}
					}
				}
				case '(':
				{
					iParenthesis++;
				}
				case ')':
				{
					iParenthesis--;
				}
			}

			if ( j < charsmax( szFuncArgs[] ) )
			{
				szFuncArgs[ iArgCount ][ j ] = szFunction[ i ];
				j++;
			}
		}

		if ( szFuncArgs[ 0 ][ 0 ] != EOS )
			iArgCount++;

		for ( new Int:i; i < iArgCount; i++ )
		{
			trim( szFuncArgs[ i ] );
		}
	}

	trim( szFuncName );
/*
	pokemod_debug( "ScriptFunction: ^"%s^"", szFuncName );
	for ( new Int:i; i < iArgCount; i++ )
	{
		pokemod_debug( "ScriptArgument %d: ^"%s^"", i, szFuncArgs[ i ] );
	}
*/

	new SCRIPTFUNC:iFuncNum = SCRIPTFUNC_NULL;
	TrieGetCell( g_iFunctionTrie, szFuncName, iFuncNum );

	new Int:iFuncReturn;

	switch( iFuncNum )
	{
		//Functions used to check things
		case SCRIPTFUNC_HavePoke:
		{
			if ( equali( szFuncArgs[ 0 ], "Any" ) )
			{
				if ( g_iPlayerPokemonStats[ iPlayer ][ 0 ][ POKESTAT_POKEMON ] > 0 )
					iFuncReturn = true;
			}
			else if ( equali( szFuncArgs[ 0 ], "None" ) )
			{
				if ( g_iPlayerPokemonStats[ iPlayer ][ 0 ][ POKESTAT_POKEMON ] <= 0 )
					iFuncReturn = true;
			}
			else
			{
				iFuncReturn = ( PlayerHasPokemon( iPlayer, NameToPoke( szFuncArgs[ 0 ] ) ) != -1 );
			}
		}
		case SCRIPTFUNC_HaveItem:
		{
			new Int:iItemID = NameToItem( szFuncArgs[ 0 ] );
			iFuncReturn = ( iItemID > -1 && PlayerHasItem( iPlayer, iItemID ) != -1 );
		}
		case SCRIPTFUNC_HaveSkill:
		{
			new POKESKILL:skillID = NameToSkill( szFuncArgs[ 0 ] );
			if ( g_iScriptPokeNum != -1 )
			{
				iFuncReturn = ( PlayerHasSkill( iPlayer, g_iScriptPokeNum, skillID ) != -1 );
			}
			else
			{
				for ( new Int:pokeNum; pokeNum < POKEMON_PER_PLAYER; pokeNum++ )
				{
					if ( PlayerHasSkill( iPlayer, pokeNum, skillID ) != -1 )
						iFuncReturn++;
				}
			}
		}
		case SCRIPTFUNC_HaveSpecial:
		{
			iFuncReturn = PlayerHasSpecial( iPlayer, szFuncArgs[ 0 ] );
		}
		case SCRIPTFUNC_Version:
		{
			iFuncReturn = ( containi( szAllArgs, szPokeModVersion[ PlayerGetInfo( iPlayer, PINFO_VERSION ) ] ) != -1 );
		}
		case SCRIPTFUNC_Chance:
		{
			iFuncReturn = Chance( str_to_num( szFuncArgs[ 0 ] ) );
		}
		case SCRIPTFUNC_AdminFlag:
		{
			new Int:iFlags = read_flags( szFuncArgs[ 0 ] );
			if ( ( iFlags == ADMIN_ADMIN && is_user_admin( iPlayer ) )
			|| ( ( get_user_flags( iPlayer ) & iFlags ) == iFlags )
			|| ( iFlags == ADMIN_ALL ) )
			{
				iFuncReturn = 1;
			}
		}

		//Functions dealing with menus
		case SCRIPTFUNC_CreateMenu:
		{
			if ( g_iScriptMenu == -1 )
			{
				g_iScriptMenu = MenuCreate( szFuncArgs[ 0 ] );
				MenuExit( iPlayer, g_iScriptMenu, .bExitOption = false );
				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_MenuItem:
		{
			if ( g_iScriptMenu != -1 )
			{
				MenuAddItem( g_iScriptMenu, szFuncArgs[ 1 ], szFuncArgs[ 0 ] );
				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_MenuShopItem:
		{
			new Int:iItemID = NameToItem( szFuncArgs[ 1 ] );
			new String:szItem[ NAME_LEN ];
			ItemToName( iItemID, szItem, charsmax(szItem) );

			new Int:paymentItem = NameToItem( szFuncArgs[ 2 ] );
			if ( paymentItem == -1 )
				copy( szFuncArgs[ 2 ], charsmax(szFuncArgs[]), "$" );
			else
				ItemToName( paymentItem, szFuncArgs[ 2 ], charsmax(szFuncArgs[]) );

			new Int:paymentCost = str_to_num( szFuncArgs[ 3 ] );
			if ( !paymentCost )
			{
				paymentCost = str_to_num( ItemInfo( iItemID, "Price" ) );
			}

			if ( paymentCost )
			{
				new String:szData[ 64 ];
				formatex( szData, charsmax(szData), "BuyItem( %s, %d, %d ) Function( %s )", szItem, szFuncArgs[ 2 ], paymentCost, szFuncArgs[ 0 ] );
				MenuAddItem( g_iScriptMenu, szData, "%s \R%d %s", szItem, paymentCost, szFuncArgs[ 2 ] );

				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_MenuExit:
		{
			if ( g_iScriptMenu != -1 )
			{
				MenuExit( iPlayer, g_iScriptMenu, .bExitOption = true );
				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_ShowCustomMenu:
		{
			if ( g_iScriptMenu != -1 )
			{
				MenuDisplay( iPlayer, g_iScriptMenu, MENU_MAIN );
				g_iScriptMenu = -1;
				iFuncReturn = 1;
			}
		}

		//Functions that actually do something
		case SCRIPTFUNC_BuyItem:
		{
			new Int:paymentItem = NameToItem( szFuncArgs[ 1 ] );
			new Int:paymentCost = str_to_num( szFuncArgs[ 2 ] );
			new bool:bBought = false;

			if ( paymentItem == -1 )
			{
				new Int:iMoney = PlayerGetMoney( iPlayer );
				if ( iMoney < paymentCost )
				{
					client_print( iPlayer, print_center, "You do not have enough money" );
				}
				else
				{
					PlayerSetMoney( iPlayer, iMoney - paymentCost );
					bBought = true;
				}
			}
			else
			{
				new Int:itemAmount;
				PlayerHasItem( iPlayer, paymentItem, itemAmount );
				if ( itemAmount < paymentCost )
				{
					client_print( iPlayer, print_center, "You do not have enough %s", ItemToName( paymentItem ) );
				}
				else
				{
					PlayerGiveItem( iPlayer, paymentItem, itemAmount - paymentCost );
					bBought = true;
				}
			}

			if ( bBought )
			{
				new Int:iItemID = NameToItem( szFuncArgs[ 0 ] );
				PlayerGiveItem( iPlayer, iItemID, 1 );
				DHudEventMessage( iPlayer, "You bought a %s.", ItemToName( iItemID ) );

				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_GiveItem:
		{
			new Int:iItemID = NameToItem( szFuncArgs[ 0 ] );
			new Int:iAmount = max( str_to_num( szFuncArgs[ 1 ] ), 1 );
			PlayerGiveItem( iPlayer, iItemID, iAmount );
			g_iScriptItemsGiven += iAmount;
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_FindItem:
		{
			new Int:iItemID = NameToItem( szFuncArgs[ 0 ] );
			new Int:iAmount = max( str_to_num( szFuncArgs[ 2 ] ), 1 );
			PlayerGiveItem( iPlayer, iItemID, iAmount );
			g_iScriptItemsGiven += iAmount;
			if ( iAmount == 1 )
				DHudEventMessage( iPlayer, "You found a %s.", ItemToName( iItemID ) );
			else
				DHudEventMessage( iPlayer, "You found %d %s.", iAmount, ItemToName( iItemID ) );
			iFuncReturn = iAmount;
		}
		case SCRIPTFUNC_LearnSkill:
		{
			new Int:pokeNum = ( g_iScriptPokeNum == -1 ) ? PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM ) : g_iScriptPokeNum;
			new POKESKILL:skillID = NameToSkill( szFuncArgs[ 0 ] );
			SkillLearn( iPlayer, pokeNum, skillID );
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_BuyPoke:
		{
			new Int:iPokeID = NameToPoke( szFuncArgs[ 0 ] );
			new Int:iLevel = max( str_to_num( szFuncArgs[ 1 ] ), 1 );
			new Int:iCost = str_to_num( szFuncArgs[ 2 ] );
			new Int:iPlayerMoney = PlayerGetMoney( iPlayer );

			if ( iCost <= iPlayerMoney )
			{
				PlayerSetMoney( iPlayer, iPlayerMoney - iCost );
				PlayerGivePoke( iPlayer, iPokeID, iLevel );
				DHudEventMessage( iPlayer, "You bought %s.", PokeToName( iPokeID ) );

				iFuncReturn = 1;
			}
			else
			{
				DHudEventMessage( iPlayer, "You do not have enough money." );
			}
		}
		case SCRIPTFUNC_GivePoke:
		{
			new Int:iPokeID = NameToPoke( szFuncArgs[ 0 ] );
			new Int:iLevel = max( str_to_num( szFuncArgs[ 1 ] ), 1 );
			iFuncReturn = PlayerGivePoke( iPlayer, iPokeID, iLevel );
		}
		case SCRIPTFUNC_GiveXP:
		{
			new Int:iPokeNum = ( g_iScriptPokeNum == -1 ) ? PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM ) : g_iScriptPokeNum;
			new Int:iXPAmount = str_to_num( szFuncArgs[ 0 ] );
			PlayerGiveXP( iPlayer, iPokeNum, iXPAmount );
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_GiveLevel:
		{
			new Int:iPokeNum = ( g_iScriptPokeNum == -1 ) ? PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM ) : g_iScriptPokeNum;
			new Int:iLevel = g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_LEVEL ];
			if ( iLevel != g_iMaxLevel )
			{
				new Int:iLevels = str_to_num( szFuncArgs[ 0 ] );
				new Int:xpType = g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_XP_TYPE ];
				new Int:xpAmount = LevelToXP( iLevel + iLevels - 1, xpType ) - g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_XP ];

				PlayerGiveXP( iPlayer, iPokeNum, xpAmount );
				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_Evolve:
		{
			new Int:iPokeNum = ( g_iScriptPokeNum == -1 ) ? PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM ) : g_iScriptPokeNum;
			new Int:iEvolvePokeID = NameToPoke( szFuncArgs[ 0 ] );
			new Int:iMenu = MenuCreate( "%s is trying to evolve into %s", PokeNumToName( iPokeNum, .playerID = iPlayer ), PokeToName( iEvolvePokeID ) );

			new String:szData[ 32 ];
			formatex( szData, charsmax(szData), "%d %d", iPokeNum, iEvolvePokeID );
			MenuAddItem( iMenu, szData, "Evolve to %s", PokeToName( iEvolvePokeID ) );

			MenuAddItem( iMenu, MENU_FAKE_EXIT, "\rBlock Evolve" );

			MenuExit( iPlayer, iMenu, .bExitOption = false );
			MenuDisplay( iPlayer, iMenu, MENU_EVOLVE );
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_GiveSpecial:
		{
			iFuncReturn = PlayerGiveSpecial( iPlayer, szFuncArgs[ 0 ] );
		}
		case SCRIPTFUNC_TakeSpecial:
		{
			iFuncReturn = PlayerTakeSpecial( iPlayer, szFuncArgs[ 0 ] );
		}
		case SCRIPTFUNC_PokeCenter:
		{
			PokemonHeal( iPlayer, -1, -1, .bRevive = true );
			PokemonResetPP( iPlayer, -1 );

			new Int:iLocation = PlayerGetInfo( iPlayer, PINFO_LOCATION );
			PlayerSetInfo( iPlayer, PINFO_LAST_POKECENTER, iLocation );

			new String:szLocation[ NAME_LEN ];
			LocationToName( iLocation, szLocation, charsmax(szLocation) );
			player_set_data( iPlayer, "PokeMod", "LastPokeCenter", szLocation );

			SoundPokeCenter( iPlayer );
			DHudEventMessage( iPlayer, "Your Pokemon are fighting fit!" );

			iFuncReturn = 1;
		}
		case SCRIPTFUNC_HealHP:
		{
			new Int:iPokeNum = ( g_iScriptPokeNum == -1 ) ? PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM ) : g_iScriptPokeNum;
			new Int:iHealAmount = str_to_num( szFuncArgs[ 0 ] );

			new Int:iPokesHealed = PokemonHeal( iPlayer, iPokeNum, iHealAmount, .bRevive = false );

			new Float:fMaxHealth; pev( iPlayer, pev_max_health, fMaxHealth );
			new Int:iHealth = get_user_health( iPlayer );
			new Int:iMaxHealth = floatround( fMaxHealth );

			if ( iHealth < iMaxHealth )
			{
				set_user_health( iPlayer, min( iHealth + iHealAmount, iMaxHealth ) );
				iPokesHealed++;
			}

			if ( iPokesHealed )
				SoundCure( iPlayer );

			iFuncReturn = iPokesHealed;
		}
		case SCRIPTFUNC_CureStatus:
		{
			new Int:iCures;
			if ( equali( szFuncArgs[ 0 ], "All" ) )
			{
				for ( new PM_STATUS:iStatNum; iStatNum < PM_STATUS; iStatNum++ )
				{
					if ( StatusCure( iPlayer, iStatNum, false ) )
						iCures++;
				}
				DHudEventMessage( iPlayer, "You have been cured of all statuses for the round" );
			}
			else
			{
				if ( StatusCure( iPlayer, NameToStatus( szFuncArgs[ 0 ] ), true ) )
					iCures++;
			}

			if ( iCures )
				SoundCure( iPlayer );
			iFuncReturn = iCures;
		}
		case SCRIPTFUNC_ShowMenu:
		{
			if ( equali( szFuncArgs[ 0 ], "Main" ) )
			{
				MenuMain( iPlayer, .bForceDisplay = false );
				iFuncReturn = 1;
			}
			else if ( equali( szFuncArgs[ 0 ], "Move" ) )
			{
				LocationMenu( iPlayer );
				iFuncReturn = 1;
			}
			else if ( equali( szFuncArgs[ 0 ], "PC" ) )
			{
				MenuPC( iPlayer );
				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_Function:
		{
			ScriptCustomFunction( iPlayer, szFuncArgs[ 0 ], szFuncArgs[ 1 ], szFuncArgs[ 2 ], szFuncArgs[ 3 ], szFuncArgs[ 4 ] );
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_TeleportTo:
		{
			new Int:iLocation = NameToLocation( szFuncArgs[ 0 ] );
			if ( iLocation <= -1 )
			{
				pokemod_debug( "Invalid location (%s) to teleport to.", szFuncArgs[ 0 ] );
			}

			else if ( PlayerGetInfo( iPlayer, PINFO_ENCOUNTER_POKEMON ) > 0 )
			{
				DHudEventMessage( iPlayer, "Cannot travel while fighting." );
			}

			else
			{
				PlayerSetInfo( iPlayer, PINFO_LOCATION, iLocation );
				PlayerSetInfo( iPlayer, PINFO_TRAVELING, 0 );

				DHudEventMessage( iPlayer, "You arrived at %s.", LocationToName( iLocation ) );

				MenuMain( iPlayer, .bForceDisplay = false );
				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_MoveTo:
		{
			new Int:iLocation = NameToLocation( szFuncArgs[ 0 ] );
			if ( iLocation <= -1 )
			{
				pokemod_debug( "Invalid location (%s) to move to.", szFuncArgs[ 0 ] );
			}

			else
			{
				new Int:iTime = max( 5, str_to_num( szFuncArgs[ 1 ] ) );

				if ( PlayerGetInfo( iPlayer, PINFO_ENCOUNTER_POKEMON ) > 0 )
				{
					DHudEventMessage( iPlayer, "You will travel to %s after the fight.", szFuncArgs[ 0 ] );
				}
				else
				{
					DHudEventMessage( iPlayer, "You started traveling to %s. (%d seconds)", szFuncArgs[ 0 ], iTime );
				}

				PlayerSetInfo( iPlayer, PINFO_TRAVEL_TO, iLocation );
				PlayerSetInfo( iPlayer, PINFO_TRAVELING, 1 );

				new Array:playerItems = g_iPlayerItems[ iPlayer ];
				new Int:size = ArraySize( playerItems ) / _:PLAYER_ITEM;
				if ( size )
				{
					new Int:iTempTravelPercent, Int:iTravelItem, Int:iProp;
					new Int:iTravelTimePercent = 100;

					for ( new Int:iItemID, Int:itemNum, String:szName[ NAME_LEN ], String:szData[ 16 ];
						itemNum < size;
						itemNum++
					){
						iItemID = ArrayGetCell( playerItems, ( ( itemNum * _:PLAYER_ITEM ) + _:ITEM_ID ) );

						iProp = ArrayGetCell( g_iItemProps, iItemID );
						if ( !( iProp & ( 1 << _:ITEM_TRAVEL_SPEED ) ) )
							continue;

						ItemToName( iItemID, szName, charsmax(szName) );
						nfv_get_data( FileName( FILE_ITEMS ), szName, "TravelSpeed", szData, charsmax(szData), '#' );
						iTempTravelPercent = str_to_num( szData );
						if ( iTempTravelPercent < iTravelTimePercent )
						{
							iTravelItem = iItemID;
							iTempTravelPercent = iTravelTimePercent;
						}
					}

					iProp = ArrayGetCell( g_iItemProps, iTravelItem );
					if ( !( iProp & ( 1 << _:ITEM_UNLIMITED_USE ) ) )
					{
						PlayerGiveItem( iPlayer, iTravelItem, -1 );
					}

					iTime = ( iTravelTimePercent * iTime ) / 100;
				}

				PlayerSetInfo( iPlayer, PINFO_TRAVEL_TIME, iTime );

				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_MoveUpdate:
		{
			new Int:iLocation = PlayerGetInfo( iPlayer, PINFO_TRAVEL_TO );
			if ( iLocation <= -1 || !PlayerGetInfo( iPlayer, PINFO_TRAVELING ) )
			{
			}

			else if ( PlayerGetInfo( iPlayer, PINFO_ENCOUNTER_POKEMON ) > 0 )
			{
				DHudEventMessage( iPlayer, "Cannot travel while fighting." );
			}

			else
			{
				PlayerSetInfo( iPlayer, PINFO_LOCATION, iLocation );
				PlayerSetInfo( iPlayer, PINFO_TRAVELING, 0 );
				PlayerSetInfo( iPlayer, PINFO_TRAVEL_TIME, 0 );

				new String:szLocation[ NAME_LEN ];
				LocationToName( iLocation, szLocation, charsmax(szLocation) );
				player_set_data( iPlayer, "PokeMod", "Location", szLocation );

				DHudEventMessage( iPlayer, "You arrived at %s.", szLocation );

				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_MoveStop:
		{
			PlayerSetInfo( iPlayer, PINFO_TRAVELING, 0 );

			new Int:iLocation = PlayerGetInfo( iPlayer, PINFO_TRAVEL_TO );
			if ( iLocation > -1 )
				DHudEventMessage( iPlayer, "You are no longer traveling to %s.", LocationToName( iLocation ) );

			MenuMain( iPlayer, .bForceDisplay = false );
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_GiveToPoke:
		{
			PokemonGiveItem( iPlayer, -1, g_iScriptItem, szFuncArgs[ 0 ] );
			g_iScriptItemUsed++;
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_EncounterPokemon:
		{
			new Int:pokeID = NameToPoke( szFuncArgs[ 0 ] );
			new Int:iLevelMin = str_to_num( szFuncArgs[ 1 ] );

			if ( pokeID > 0 && iLevelMin > 0 )
			{
				new Int:iLevelMax;

				new Int:iDashLoc = contain( szFuncArgs[ 1 ], "-" );
				if ( iDashLoc != -1 )
				{
					iLevelMax = str_to_num( szFuncArgs[ 1 ][ iDashLoc + 1 ] );
					if ( iLevelMax == iLevelMin )
						iLevelMax = 0;
				}

				new Int:iChance = str_to_num( szFuncArgs[ 2 ] );
				for ( new Int:i; i < iChance; i++ )
				{
					ArrayPushCell( g_iScriptEncounters, pokeID );
					if ( iLevelMax )
					{
						ArrayPushCell( g_iScriptEncounters, random_num( iLevelMin, iLevelMax ) );
					}
					else
					{
						ArrayPushCell( g_iScriptEncounters, iLevelMin );
					}
				}
				iFuncReturn = iChance;
			}
		}
		case SCRIPTFUNC_FightEncounter:
		{
			iFuncReturn = EncounterFight( iPlayer );
		}
		case SCRIPTFUNC_RemoveEncounter:
		{
			if ( PlayerGetInfo( iPlayer, PINFO_ENCOUNTER_POKEMON ) )
			{
				PlayerSetInfo( iPlayer, PINFO_ENCOUNTER_POKEMON, 0 );
				iFuncReturn = 1;
			}
		}
		case SCRIPTFUNC_Fighting:
		{
			iFuncReturn = ( PlayerGetInfo( iPlayer, PINFO_ENCOUNTER_POKEMON ) > 0 );
		}
		case SCRIPTFUNC_Surfing:
		{
			iFuncReturn = PlayerGetInfo( iPlayer, PINFO_SURFING );
		}
		case SCRIPTFUNC_EventPrint:
		{
			DHudEventMessage( iPlayer, "%s", szFuncArgs[ 0 ] );
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_EventPrintAll:
		{
			DHudEventMessage( 0, "%s", szFuncArgs[ 0 ] );
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_Print:
		{
			client_print( iPlayer, print_chat, "%s", szFuncArgs[ 0 ] );
			iFuncReturn = 1;
		}
		case SCRIPTFUNC_ViewMap:
		{
			PlayerViewMap( iPlayer, NameToLocation( szFuncArgs[ 0 ] ) );
			iFuncReturn = 1;
		}

		case SCRIPTFUNC_Random:
		{
			if ( iArgCount )
			{
				iFuncReturn = ScriptDoLine( iPlayer, szFuncArgs[ random( iArgCount ) ] );
			}
		}

		case SCRIPTFUNC_RandomNum:
		{
			iFuncReturn = random_num( str_to_num( szFuncArgs[0] ), str_to_num( szFuncArgs[1] ) );
		}

		case SCRIPTFUNC_ItemUsed:
		{
			g_iScriptItemUsed++;
			iFuncReturn = 1;
		}

		case SCRIPTFUNC_StatBoost:
		{
			new PM_STAT:iPlayerStat = NameToPlayerStat( szFuncArgs[ 0 ] );
			new Int:iBoost = str_to_num( szFuncArgs[ 1 ] );
			if ( iPlayerStat != STAT_NULL && iBoost )
			{
				new PM_POKESTAT:iPokeStat = PlayerStatToPokeStat( iPlayerStat );
				new Int:iPokeNum = PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM );
				iFuncReturn = StatAddAmount( iPlayer, iPokeNum, iPokeStat, iBoost );
			}
		}

		case SCRIPTFUNC_Command:
		{
			iFuncReturn = client_cmd( iPlayer, szFuncArgs[ 0 ] );
		}

		case SCRIPTFUNC_If:
		{
			if ( ScriptDoLine( iPlayer, szFuncArgs[ 0 ] ) )
			{
				iFuncReturn = ScriptDoLine( iPlayer, szFuncArgs[ 1 ] );
			}
			else if ( iArgCount > 2 )
			{
				iFuncReturn = ScriptDoLine( iPlayer, szFuncArgs[ 2 ] );
			}
		}

		case SCRIPTFUNC_And:
		{
			for ( new Int:iArg; iArg < iArgCount; iArg++ )
			{
				if ( ScriptDoLine( iPlayer, szFuncArgs[ iArg ] ) )
				{
					iFuncReturn++;
				}
				else
				{
					iFuncReturn = 0;
					break;
				}
			}
		}

		case SCRIPTFUNC_Or:
		{
			for ( new Int:iArg; iArg < iArgCount; iArg++ )
			{
				if ( ScriptDoLine( iPlayer, szFuncArgs[ iArg ] ) )
				{
					iFuncReturn++;
				}
			}
		}

		case SCRIPTFUNC_RegisterSayCmd:
		{
			strtoupper( szFuncArgs[ 0 ] );
			TrieSetString( g_iSayCmdsTrie, szFuncArgs[ 0 ], szFuncArgs[ 1 ] );
		}

		case SCRIPTFUNC_DisplayMOTD:
		{
			show_motd( iPlayer, szFuncArgs[ 0 ], szFuncArgs[ 1 ] );
		}

		case SCRIPTFUNC_CUSTOM:
		{
			new String:szForwardName[ NAME_LEN ];
			formatex( szForwardName, charsmax(szForwardName), "PokeScript%s", szFuncName );

			new Int:iCustomScriptForward = CreateMultiForward( szForwardName, ET_STOP, FP_CELL, FP_STRING, FP_STRING, FP_STRING, FP_STRING, FP_STRING );
			if ( iCustomScriptForward < 0 )
			{
				pokemod_debug( "Could not find public custom script function %s", szForwardName );

				pokemod_funcend_debug( "ScriptFunction Invalid PokeScript Function" );
				return 0;
			}

			ExecuteForward( iCustomScriptForward, iFuncReturn, iPlayer, szFuncArgs[ 0 ], szFuncArgs[ 1 ], szFuncArgs[ 2 ], szFuncArgs[ 3 ], szFuncArgs[ 4 ] );
			DestroyForward( iCustomScriptForward );

/*
			//Cannot use this because it does not go to other plugins
			new Int:iFuncID = get_func_id( szForwardName );
			if ( iFuncID > -1 )
			{
				callfunc_begin_i( iFuncID );

				for ( new Int:i; i < iArgCount; i++ )
					callfunc_push_str( szFuncArgs[ i ], false );

				return callfunc_end();
			}
			pokemod_debug( "Could not find public custom script function %s", szForwardName );
			return 0;
*/
		}

		default:
		{
			pokemod_debug( "Invalid Script Function: '%s'", szFuncName );
		}
	}

	#undef szAllArgs

	pokemod_funcend_debug( "ScriptFunction %d", iFuncReturn );
	return iFuncReturn;
}

PM_FUNC_NORM_ATTRIB ScriptCustomFunction( Int:iPlayer, String:szFunctionName[], String:szArg1[]="", String:szArg2[]="", String:szArg3[]="", String:szArg4[]="" )
{
	pokemod_funcstart_debug( "ScriptCustomFunction" );

	new Int:iFileHandle = fopen( FileName( FILE_FUNCTIONS ), "rt" );
	if ( !FileSeekToName( iFileHandle, szFunctionName ) )
	{
		fclose( iFileHandle );
		pokemod_debug( "Could not find Custom Function #%s", szFunctionName );

		pokemod_funcend_debug( "ScriptCustomFunction Could not find Custom Function" );
		return 0;
	}

	new String:szData[ 128 ];

	while ( !feof( iFileHandle ) )
	{
		fgets( iFileHandle, szData, charsmax(szData) );
		trim( szData );

		if ( szData[0] == '#' )
			break;

		if ( szData[0] == ';' || ( szData[0] == '/' && szData[1] == '/' ) )
			continue;

		if ( contain( szData, "%arg" ) != -1 )
		{
			replace_all( szData, charsmax(szData), "%arg1%", szArg1 );
			replace_all( szData, charsmax(szData), "%arg2%", szArg2 );
			replace_all( szData, charsmax(szData), "%arg3%", szArg3 );
			replace_all( szData, charsmax(szData), "%arg4%", szArg4 );
		}

		ScriptDoLine( iPlayer, szData );
	}

	fclose( iFileHandle );

	pokemod_funcend_debug( "ScriptCustomFunction" );

	return 1;
}




